#include <header.h>
#include <iostream>
#include <map>
#include <set>
#include <sys/epoll.h>

using std::map;
using std::set;
using std::string;
using std::cout;
using std::endl;


int main()
{
    // 创建一个socket对象
    int listenfd = socket(AF_INET, SOCK_STREAM, 0);
    if(listenfd < 0)
    {  
        cout << "socket" << endl;
        return EXIT_FAILURE;
    }

    struct sockaddr_in serveraddr;
    memset(&serveraddr, 0, sizeof(serveraddr));
    serveraddr.sin_family = AF_INET;
    serveraddr.sin_port = htons(1280);
    serveraddr.sin_addr.s_addr = inet_addr("127.0.0.1");

    // 绑定socket对象到地址
    int ret = bind(listenfd, (struct sockaddr*)&serveraddr, sizeof(serveraddr));
    if(ret < 0)//错误处理
    {
        cout << "Bind error" << endl;
        close(listenfd); // 关闭socket
        return EXIT_FAILURE;
    }

    // 监听端口
    ret = listen(listenfd, 5);
    if(ret < 0)
    {   
        cout << "Listen error" << endl;
        close(listenfd);    //为什么关闭socket?
                            //因为监听失败了，socket没有用处了
                            //所以需要关闭它
        return EXIT_FAILURE;
    }
        
    //创建epoll实例/对象
    int epfd = epoll_create1(0);
    if(epfd < 0)
    {
        cout << "Epoll create error" << endl;
        close(listenfd);//为什么关闭socket?
                        //因为创建epoll失败了，socket没有用处了
                        //所以需要关闭它
                        //为什么创建epoll失败就要关闭socket?
                        //因为epoll和socket是相关联的，如果epoll创建失败了，socket
                        //就没有用处了，所以需要关闭它
                        //哪里显示是相关联的？
                        //因为epoll是用来监听socket的，如果epoll创建失败了，就无法监听socket了
        return EXIT_FAILURE;
    }

    //将监听的socket添加到epoll中
    struct epoll_event ev;
    memset(&ev, 0, sizeof(ev));//用之前清空
    
    //是水平触发吗？
    //只要没有显示的设置EPOLLET（边缘触发），那就是水平触发
    ev.events = EPOLLIN; //为什么是EPOLLIN?
                         //因为我们需要监听客户端的连接请求，所以需要监听读事件
                         //为什么连接请求是读事件?
                         //因为连接请求是通过accept函数来处理的，而accept函数是读取连接请求
                         //所以连接请求是读事件

    ev.data.fd = listenfd; //监听的socket
    ret = epoll_ctl(epfd, EPOLL_CTL_ADD, listenfd, &ev);
    if(ret < 0)
    {       
        cout << "Epoll ctl error" << endl;
        close(listenfd); //为什么关闭socket?
                        //因为添加到epoll失败了，socket没有用处了
                        //所以需要关闭它
        close(epfd); //为什么关闭epoll?
                     //因为epoll没有用处了，所以需要关闭它
        return EXIT_FAILURE;
    }

    map<int, int> conns; //存储客户端的socket和epoll事件的映射关系

    struct epoll_event events[1024]; //事件数组
    while(1)
    {
        int nready = epoll_wait(epfd, events, 1024, -1);
        if(nready < 0 && errno == EINTR)
        {
            continue; //如果被信号打断了，就继续等待
        }
        else if(nready < 0)
        {
            cout << "Epoll wait error" << endl;
            break; //其他错误就退出
        }else if(nready == 0)
        {
            printf("Epoll wait timeout\n");
            continue; //没有事件发生就继续等待
        }else
        {
            printf("Epoll wait nready : %d\n", nready);

            //这一步是为了什么？
            for(int i = 0;i < nready;++i)
            {
                int fd = events[i].data.fd;
                if(fd == listenfd)
                {
                    struct sockaddr_in clientaddr;
                    socklen_t len = sizeof(clientaddr);
                    int netfd = accept(listenfd,(struct sockaddr*)&clientaddr,&len);
                    if(netfd < 0)
                    {
                        cout << "accept error" << endl;
                    }
                    printf("Conn %d tcp %s:%d --> %s:%d\n",
                        netfd,
                        inet_ntoa(serveraddr.sin_addr),
                        ntohs(serveraddr.sin_port),
                        inet_ntoa(clientaddr.sin_addr),
                        ntohs(clientaddr.sin_port));

                    ev.data.fd = netfd;
                    ev.events = EPOLLIN;
                    ret = epoll_ctl(epfd, EPOLL_CTL_ADD, netfd, &ev);
                    if(ret < 0)
                    {
                        cout << "Epoll ctl add error" << endl;
                        close(netfd);
                        return EXIT_FAILURE;
                    }
                     //监听读事件
                    int timenum = time(NULL);
                    conns[netfd] = timenum; //记录连接时间
                }
                else
                {
                    char buf[1024];
                    memset(buf, 0, sizeof(buf));
                    ret = recv(fd, buf, sizeof(buf) , 0);
                    if(ret <= 0)
                    {
                       ev.data.fd = fd;
                       
                        close(fd);
                        epoll_ctl(epfd, EPOLL_CTL_DEL, fd, NULL); //从epoll中删除
                        conns.erase(fd); //从map中删除
                        continue; //继续处理下一个事件
                    }
                   
                    printf("Recv %s\n", buf);
                    conns[fd] = time(NULL); //更新连接时间
                    for(auto& conn : conns)
                    {
                        if(conn.first != fd)
                        {
                            send(conn.first, buf, ret, 0); //回显给其他客户端
                        } 
                        //printf("Conn %d time %d\n", conn.first, conn.second);
                    }   
                }
            }
        }
        int curtime = time(NULL);
        set<int> delset; //存储需要删除的连接
        for(auto& elem : conns)
        {
            if(curtime - elem.second > 30) //如果连接超过10秒没有数据
            {
                close(elem.first); //关闭连接
                delset.insert(elem.first); //加入删除集合
            }
        }
        for(auto& fd : delset)
        {
            epoll_ctl(epfd, EPOLL_CTL_DEL, fd, NULL); //从epoll中删除
            conns.erase(fd); //从map中删除
        }
        printf("Current connections: %ld\n", conns.size());

    }//end while
    return 0;
}

